package com.my.netty.promote.client;

import com.my.netty.promote.message.*;
import com.my.netty.promote.protocol.MessageCodecSharable;
import com.my.netty.promote.protocol.ProtocolFrameDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

/*
    聊天室业务，服务端启动类
 */
@Slf4j
public class ChatClient {
    public static void main(String[] args) {
        NioEventLoopGroup worker = new NioEventLoopGroup();
        LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.DEBUG);
        MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable();

        //登录完成时，NIO线程通知控制台输入线程，继续向下运行
        CountDownLatch WAIT_LOGIN = new CountDownLatch(1);
        //NIO线程返回是否登录成功
        AtomicBoolean LOGIN = new AtomicBoolean(false);

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(worker);
            bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                    //处理半包粘包
                    ch.pipeline().addLast(new ProtocolFrameDecoder());
                    //打印DEBUG日志
//                    ch.pipeline().addLast(LOGGING_HANDLER);
                    //自定义消息体编解码
                    ch.pipeline().addLast(MESSAGE_CODEC);
                    //3s如果没有向服务器发送数据，就触发写空闲时间 IdleState.READER_IDLE
                    ch.pipeline().addLast(new IdleStateHandler(0, 3, 0));
                    ch.pipeline().addLast(new ChannelDuplexHandler(){
                        @Override
                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                            IdleStateEvent event = (IdleStateEvent) evt;
                            if (event.state() == IdleState.WRITER_IDLE) {
                                //触发写空闲事件，测试聊天时注释这行日志
                                log.debug("已经3s没向服务器发数据了，发送一个心跳包");
                                ctx.writeAndFlush(new PingMessage());
                            }
                        }
                    });
                    //客户端一连接建立，就向服务端发送登录请求
                    ch.pipeline().addLast("client handler", new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            //启动线程接收用户输入
                            new Thread(() -> {
                                Scanner sc = new Scanner(System.in);
                                System.out.println("请输入用户名：");
                                String username = sc.nextLine();
                                System.out.println("请输入用密码：");
                                String password = sc.nextLine();
                                //登录
                                LoginRequestMessage message = new LoginRequestMessage(username, password);
                                //发送消息
                                ctx.writeAndFlush(message);

                                System.out.println("等待后续操作...");
                                try {
                                    WAIT_LOGIN.await();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                if (!LOGIN.get()) {
                                    System.out.println("登录失败！");
                                    //关闭channel，结束线程
                                    ctx.channel().close();
                                    return;
                                }
                                System.out.println("登录成功，用户名:" + message.getUsername());
                                //登录成功，继续接收输入
                                while (true) {
                                    printMenu();
                                    //假设输入的命令都是正确的
                                    String command = sc.nextLine();
                                    String[] s = command.split(" ");
                                    switch (s[0]) {
                                        case "send":
                                            ctx.writeAndFlush(new ChatRequestMessage(username, s[1], s[2]));
                                            break;
                                        case "gsend":
                                            ctx.writeAndFlush(new GroupChatRequestMessage(username, s[1], s[2]));
                                            break;
                                        case "gcreate":
                                            Set<String> members = new HashSet<>(Arrays.asList(s[2].split(",")));
                                            //加入自己
                                            members.add(username);
                                            ctx.writeAndFlush(new GroupCreateRequestMessage(s[1], members));
                                            break;
                                        case "gmembers":
                                            ctx.writeAndFlush(new GroupMembersRequestMessage(s[1]));
                                            break;
                                        case "gjoin":
                                            ctx.writeAndFlush(new GroupJoinRequestMessage(username, s[1]));
                                            break;
                                        case "gquit":
                                            ctx.writeAndFlush(new GroupQuitRequestMessage(username, s[1]));
                                            break;
                                        case "quit":
                                            ctx.channel().close();
                                            return;
                                    }

                                }
                            }, "system in").start();
                        }

                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            log.info("接收服务端消息：{}", msg);
                            if (msg instanceof LoginResponseMessage) {
                                //是登录返回消息
                                LoginResponseMessage responseMessage = (LoginResponseMessage) msg;
                                //通知控制台线程
                                LOGIN.set(responseMessage.isSuccess());
                                WAIT_LOGIN.countDown();
                            }
                        }

                        @Override
                        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                            //客户端断开时
                            log.info("连接已断开");
                        }

                        @Override
                        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                            //出现异常时
                            log.info("出现异常{}，连接断开", cause.getMessage());
                        }
                    });
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("localhost", 8080).sync();
            //closeFuture().sync()可以让主线程一直阻塞直到channel关闭
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("client error", e);
        } finally {
            worker.shutdownGracefully();
        }
    }

    private static void printMenu() {
        //打印功能菜单
        System.out.println("==============================");
        System.out.println("send [username] [content]");
        System.out.println("gsend [groupname] [content]");
        System.out.println("gcreate [groupname] [member1,member2,...]");
        System.out.println("gmembers [groupname]");
        System.out.println("gjoin [groupname]");
        System.out.println("gquit [groupname]");
        System.out.println("quit");
        System.out.println("==============================");
    }
}
